using System;
using System.IO;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Threading;

public class DNGSStreamReader
{
    Stream input;

    public DNGSStreamReader(Stream input)
    {
        this.input = input;
    }

    public void readAllTo(MemoryStream foo, FileHeader header)
    {
        int count = (int)header.realSize;

        for (int i = 0; i <= input.Length / 56; i++)
        {
            byte[] bar = read();
            foo.Write(bar, 0, count % 8);
            
            count -= 8;
        }
        foo.Close();
    }

    
    public MyMemoryStream readAll()
    {
        return new MyMemoryStream();
    }

    public MyMemoryStream readAll(FileHeader header)
    {
        int count = (int)header.realSize;
        int fileSize = count;
        MyMemoryStream foo = new MyMemoryStream(count);

        for (int i = 0; i <= (header.compressedSize - 1) / 7; i++)
        {
            byte[] bar = read();
            //Console.Error.WriteLine("Modulus: " + count % 8);

            if (count > 8)
            {
                foo.Write(bar, 0, 8);
                count -= 8;
            }
            else
            {
                foo.Write(bar, 0, count);
            }

            //Console.Error.WriteLine(foo.Length);
        }

        //Console.Error.WriteLine(BitConverter.ToString(foo.ToArray()));
        foo.Close();
        return foo;
    }

    public byte[] read()
    {
        byte[] buffer = new byte[7];
        byte[] buffer2 = new byte[8];

        int count = input.Read(buffer, 0, 7);

        buffer2[0] += (byte)(buffer[0] >> 1);
        buffer2[0] &= unchecked((byte)(~(1 << 7)));

        buffer2[1] += (byte)(buffer[1] >> 2);
        buffer2[1] += (byte)((byte)(buffer[0] << 7) / 2);
        buffer2[1] &= unchecked((byte)(~(1 << 7)));

        buffer2[2] += (byte)(buffer[2] >> 3);
        buffer2[2] += (byte)((byte)(buffer[1] << 6) / 2);
        buffer2[2] &= unchecked((byte)(~(1 << 7)));

        buffer2[3] += (byte)(buffer[3] >> 4);
        buffer2[3] += (byte)((byte)(buffer[2] << 5) / 2);
        buffer2[3] &= unchecked((byte)(~(1 << 7)));

        buffer2[4] += (byte)(buffer[4] >> 5);
        buffer2[4] += (byte)((byte)(buffer[3] << 4) / 2);
        buffer2[4] &= unchecked((byte)(~(1 << 7)));

        buffer2[5] += (byte)(buffer[5] >> 6);
        buffer2[5] += (byte)((byte)(buffer[4] << 3) / 2);
        buffer2[5] &= unchecked((byte)(~(1 << 7)));

        buffer2[6] += (byte)(buffer[5] << 1);
        buffer2[6] += (byte)(buffer[6] >> 7);
        buffer2[6] &= unchecked((byte)(~(1<<7)));

        buffer2[7] += (byte)buffer[6];
        buffer2[7] &= unchecked((byte)(~(1 << 7)));


        return buffer2;
    }
}

public class MyFileStream2 : MyFileStream
{
    public MyFileStream2(string path, FileMode mode, FileAccess access) : base(path, mode, access)
    {

    }

    public MyMemoryStream compress()
    {
        Seek(0, SeekOrigin.Begin);
        MyMemoryStream mms = new MyMemoryStream();
        DNGSStreamWriter writer = new DNGSStreamWriter(mms);
        writer.writeAll(this);
        return mms;
    }

    public bool isCompressable()
    {
        Seek(0, SeekOrigin.Begin);

        for (int i = 0; i < this.Length; i++)
        {
            int onzin = ReadByte();

            if (onzin > 127)
                return false;
        }

        Seek(0, SeekOrigin.Begin);
        return true;

    }
}

public class DNGSFileWriter : FileWriter
{
    public DNGSFileWriter(FileInfo fi, Stream outputStream, Object slot) : base(fi, outputStream, slot) { }

    public override void writeSingle()
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        FileHeaderFactory factory = new FileHeaderFactory();
        FileHeader header = factory.nieuw(fi);
        header.compressionType = 0;
        MyFileStream2 inputFile = new MyFileStream2(fi.FullName, FileMode.Open, FileAccess.Read);
        header.md5sum = inputFile.md5().md5sum;
        inputFile.Seek(0, SeekOrigin.Begin);
        MemoryStream compressedStream = new MemoryStream();

        if (inputFile.isCompressable())
        {
            compressedStream = inputFile.compress();
            header.compressionType = 2;
        }
        else
        {
            inputFile.CopyTo(compressedStream); 
            header.compressionType = 0;
        }

        compressedStream.Seek(0, SeekOrigin.Begin);
        //Console.Error.WriteLine(BitConverter.ToString(compressedStream.))
        header.compressedSize = compressedStream.Length;
        MemoryStream headerStream = header.getStream();

        lock (slot)
        {
            outputStream.Write(headerStream.ToArray(), 0, (int)headerStream.Length);           
            compressedStream.CopyTo(outputStream);
        }

        compressedStream.Close();
        outputStream.Flush();
        inputFile.Close();
        Console.Error.WriteLine("{0} completed by {1} in {2}", fi.ToString(), Thread.CurrentThread.Name, sw.Elapsed);
    }
}

public class DNGSArchiveFactory : ArchiveFactory
{
    private Object slot = new Object();

    public DNGSArchiveFactory() : base() { }
    public DNGSArchiveFactory(BackgroundWorker worker) : base(worker) { }
    public DNGSArchiveFactory(BackgroundWorker worker, Pool pool) : base(worker, pool) { }

    public override void write(Stream outputStream)
    {
        Object slot = new Object();

        for (int i = 0; i < files.Count; i++)
        {
            FileWriter fw = new DNGSFileWriter(new FileInfo(files[i]), outputStream, slot);
            Action fileAction = (() =>
            {
                fw.writeSingle();
            });
            pool.QueueTask(() => fileAction());
        }

        pool.quit();
        Console.Error.WriteLine("close");
        
    }
}

public class DNGSStreamWriter
{
    Stream output;

    public DNGSStreamWriter(Stream output) { this.output = output; }

    public void writeAll(Stream ms)
    {
        for (int i = 0; i <= (ms.Length - 1) / 8; i++)
        {
            //Console.Error.WriteLine("Hit");
            byte[] buffer = new byte[8];
            int count = ms.Read(buffer, 0, 8);
            write(buffer);
        }
    }

    public void write(byte[] buffer)
    {
        //Console.Error.WriteLine(BitConverter.ToString(buffer));
        byte[] buffer2 = new byte[7];

        buffer2[0] = (byte)(buffer[0] << 1);
        buffer2[0] += (byte)(buffer[1] >> 6);

        buffer2[1] = (byte)(buffer[1] << 2);
        buffer2[1] += (byte)(buffer[2] >> 5);

        buffer2[2] = (byte)(buffer[2] << 3);
        buffer2[2] += (byte)(buffer[3] >> 4);

        buffer2[3] = (byte)(buffer[3] << 4);
        buffer2[3] += (byte)(buffer[4] >> 3);

        buffer2[4] = (byte)(buffer[4] << 5);
        buffer2[4] += (byte)(buffer[5] >> 2);

        buffer2[5] = (byte)(buffer[5] << 6);
        buffer2[5] += (byte)(buffer[6] >> 1);

        buffer2[6] = (byte)(buffer[6] << 7);
        buffer2[6] += (byte)(buffer[7]);

        //Console.Error.WriteLine(BitConverter.ToString(buffer2));
        output.Write(buffer2, 0, 7);
    }

}